import json
import re

class JsonPreprocessor:
    """
     一个用于在解码前清理和修复类JSON字符串的工具类。
    主要功能:
    1. 将用作字符串分隔符的反引号(`)替换为双引号(")。
    2. 修复未被正确转义的单个反斜杠。
    3. 移除可能导致错误的控制字符（如换行符、制表符等）。
    """
    def clean(self, data_string: str) -> str:
        if not isinstance(data_string, str):
            return data_string
        processed_string = data_string
        processed_string = processed_string.replace('`', '"')
        processed_string = processed_string.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ')
        processed_string = re.sub(r'\\(?!["\\/bfnrtu])', r'\\\\', processed_string)
        return processed_string


def parse_ai_json_string(raw_string: str) -> list | dict:
    """
   解析可能包含Markdown代码块的AI生成的JSON字符串。

    功能:
    1. 移除并提取由 ```json ... ``` 或 ``` ... ``` 包裹的JSON内容。
    2. 尝试直接解析。
    3. 如果解析失败，会进行一系列清理操作，然后再次尝试：
       - 将非标准的反引号(`)替换为双引号(")。
       - 修复未正确转义的单个反斜杠（例如Windows路径中的'\'）。
       - 作为最后手段，移除字符串开头和结尾的所有非JSON字符。

    Args:
        raw_string (str): 从AI获取的原始字符串。

    Returns:
        list | dict: 解析成功后的Python列表或字典。

    Raises:
        ValueError: 如果经过所有清理步骤后，字符串仍然无法解析为有效的JSON。
    """
    if not isinstance(raw_string, str):
        # 如果输入不是字符串，直接返回，或根据需要抛出错误
        return raw_string

    # 1. 优先提取Markdown代码块中的内容
    #    - r'```(?:json)?'  匹配 ``` 或 ```json
    #    - \s*              匹配任意空白符
    #    - (.*)             捕获核心的JSON内容（关键部分）
    #    - \s*```$          匹配结尾的```
    #    - re.DOTALL        使 . 能够匹配包括换行符在内的所有字符
    match = re.search(r'```(?:json)?\s*(.*)\s*```$', raw_string.strip(), re.DOTALL)
    if match:
        content_to_parse = match.group(1).strip()
    else:
        # 如果没有找到代码块，则假定整个字符串都是内容
        content_to_parse = raw_string.strip()

    # 2. 第一次尝试直接解析，通常情况下这就能成功
    try:
        return json.loads(content_to_parse)
    except json.JSONDecodeError:
        # 如果直接解析失败，进入清理流程
        pass

    # 3. 执行清理操作
    #    - 替换反引号为双引号
    cleaned_content = content_to_parse.replace('`', '"')

    #    - 修复未转义的反斜杠：查找一个'\'，但它后面不能是另一个'\'或有效的JSON转义字符
    #      这有助于修复例如 "path": "C:\Users" 这样的错误
    cleaned_content = re.sub(r'\\(?!["\\/bfnrtu])', r'\\\\', cleaned_content)

    # 4. 第二次尝试解析清理后的内容
    try:
        return json.loads(cleaned_content)
    except json.JSONDecodeError:
        # 如果仍然失败，采取更激进的“最后手段”
        # 移除开头直到第一个 '{' 或 '[' 的所有内容
        final_attempt = re.sub(r'^[^{[]*', '', cleaned_content)
        # 移除结尾直到最后一个 '}' 或 ']' 的所有内容
        final_attempt = re.sub(r'[^}\]]*$', '', final_attempt)

        try:
            return json.loads(final_attempt)
        except json.JSONDecodeError as e:
            # 如果所有尝试都失败，抛出一个信息明确的错误
            raise ValueError(f"无法将字符串解析为有效的JSON。最终解析错误: {e}")


def validate_json_keys(json_input, required_keys: list[str]) -> tuple[bool, list[str]]:
    """
    校验一个JSON字符串或字典是否完整地包含了所有必需的字段。

    :param json_input: 需要校验的JSON字符串或Python字典。
    :param required_keys: 一个包含所有必需字段名的字符串列表。
    :return: 一个元组 (is_valid, details)。
             - 如果 is_valid 为 True, `details` 是一个空列表 []。
             - 如果 is_valid 为 False, `details` 是一个包含缺失字段名或错误信息的列表。
    """
    # 步骤1: 校验 required_keys 参数的有效性
    if not isinstance(required_keys, list) or not all(isinstance(key, str) for key in required_keys):
        # 通常这里可以抛出异常，但为了保持返回格式一致性，我们返回一个错误信息
        return False, ["参数 'required_keys' 必须是一个字符串列表"]

    required_keys_set = set(required_keys)

    # 步骤2: 解析输入
    data = {}
    if isinstance(json_input, str):
        try:
            data = json.loads(json_input)
        except json.JSONDecodeError as e:
            return False, [f"JSON格式错误: {e}"]
    elif isinstance(json_input, dict):
        data = json_input
    else:
        return False, [f"输入类型错误: 期望是 str 或 dict, 实际是 {type(json_input).__name__}"]

    if not isinstance(data, dict):
        return False, [f"JSON内容必须是一个对象/字典, 实际是 {type(data).__name__}"]

    # 步骤3: 找出缺失的字段
    present_keys = set(data.keys())
    missing_keys = required_keys_set - present_keys

    # 步骤4: 返回结果
    if not missing_keys:
        return True, []
    else:
        return False, sorted(list(missing_keys))